package order

import (
	"context"
	// "fmt"

	"mall/common/globalkey"
	"mall/common/jwt"
	"mall/common/moneyx"
	"mall/common/resultx"
	"mall/common/utils/pointy"
	"mall/service/forum/api/internal/biz/checkout"
	"mall/service/forum/api/internal/lib/userrank"
	"mall/service/forum/api/internal/svc"
	"mall/service/forum/api/internal/types"
	"mall/service/user/rpc/userclient"

	"github.com/shopspring/decimal"
	// "github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type CheckoutLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewCheckoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CheckoutLogic {
	return &CheckoutLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

// promotion
//   groupon
//   wholesale
//   sales_activity
//   coupon
//   giftcard
//   points
//
// order
//   skus_amount
//   activity_amount
//   coupon_amount
//   freight
//   final_amount

func (l *CheckoutLogic) Checkout(
	req *types.CheckoutReq,
	headers *[]*types.Header,
) (resp *types.CheckoutInfo, err error) {
	userId := jwt.GetUserId(l.ctx)
	if userId == 0 {
		return nil, resultx.NewErrCode(resultx.TOKEN_EXPIRE_ERROR)
	}

	userAuthInfo, err := l.fetchUserAuth(userId)
	if err != nil {
		return nil, err
	}

	userRank := "novice"
	if userAuthInfo.RankId != nil && *userAuthInfo.RankId > 0 {
		userRankMap := l.getUserRankMap(userId)
		if userRankInfo, ok := userRankMap[*userAuthInfo.RankId]; ok {
			userRank = *userRankInfo.Name
		}
	}

	accountGems := decimal.NewFromInt(0)
	if userAuthInfo.AvailableBalance != nil {
		if val, err := moneyx.ParseDecimal(*userAuthInfo.AvailableBalance); err != nil {
			logx.Errorw("GetShoppingCart accountGems ParseDecimal err", logx.Field("detail", err.Error()))
		} else {
			accountGems = val
		}
	}
	var accountPoint uint64
	if userAuthInfo.AvailablePoint != nil {
		accountPoint = *userAuthInfo.AvailablePoint
	}

	ctx := &checkout.Context{
		Req:          req,
		Resp:         &types.CheckoutInfo{},
		Ctx:          l.ctx,
		SvcCtx:       l.svcCtx,
		UserId:       userId,
		UserRank:     userRank,
		GemRatio:     decimal.NewFromInt(int64(globalkey.GemRatio)),
		AccountGems:  accountGems,
		AccountPoint: accountPoint,
		SkuAmountMap: make(map[uint64]decimal.Decimal),
		MutexMap:     make(map[uint32]map[uint64]int64),
	}

	// 初始化空handler
	nullHandler := &checkout.NullHandler{}

	nullHandler.SetNext(&checkout.ArgumentsHandler{}).
		SetNext(&checkout.AddressInfoHandler{}).
		SetNext(&checkout.SkuInfoHandler{}).
		SetNext(&checkout.RecordInfoHandler{}).
		SetNext(&checkout.StockCheckHandler{}).
		SetNext(&checkout.PromotionInfoHandler{}).
		SetNext(&checkout.PromotionUseHandler{}).
		SetNext(&checkout.CouponInfoHandler{}).
		SetNext(&checkout.CouponUseHandler{}).
		SetNext(&checkout.ShipmentInfoHandler{}).
		SetNext(&checkout.PackageAndCleanupHandler{})

	// 开始执行业务
	if err := nullHandler.Run(ctx); err != nil {
		// 异常
		logx.Infow("Checkout Fail | Error:", logx.Field("detail", err.Error()))

		return nil, err
	}

	// 成功
	// logx.Infow("Success")

	return ctx.Resp, nil
}

func (l *CheckoutLogic) fetchUserAuth(userId uint64) (*userclient.TmpUserAuthInfo, error) {
	res, err := l.svcCtx.UserRpc.GetUserAuthByUserId(l.ctx, &userclient.GetUserAuthByUserIdReq{
		PlatformId:  pointy.GetPointer(globalkey.ThirdPartyPlatformWechatMini),
		UserId:      pointy.GetPointer(userId),
		WithRank:    pointy.GetPointer(uint32(1)),
		WithPoint:   pointy.GetPointer(uint32(1)),
		WithBalance: pointy.GetPointer(uint32(1)),
	})
	if err != nil {
		logx.Infow("Checkout fetchUserAuth UserRpc.GetUserAuthByUserId err", logx.Field("detail", err.Error()))
		return nil, resultx.NewErrCode(resultx.USER_GET_INFO_FAILED)
	}

	if res.Openid == nil {
		return nil, resultx.NewErrCode(resultx.USER_GET_INFO_FAILED)
	}

	return res, nil
}

func (l *CheckoutLogic) getUserRankMap(userId uint64) map[uint32]*types.UserRankInfo {
	userRank := userrank.NewUserRank(l.ctx, l.svcCtx)
	userRankList, err := userRank.List(userId)
	if err != nil {
		// logrus.Info(fmt.Sprintf("Checkout getUserRankMap err: %+v", err))
		logx.Errorw("Checkout getUserRankMap err", logx.Field("detail", err.Error()))
		return nil
	}

	if len(userRankList) > 0 {
		resp := make(map[uint32]*types.UserRankInfo)
		for _, v := range userRankList {
			if v.Id != nil && *v.Id > 0 {
				resp[uint32(*v.Id)] = v
			}
		}

		return resp
	}

	return nil
}
